package org.gcu.filestorage.controller;


import io.minio.*;
import io.minio.errors.*;
import io.minio.messages.Item;
import lombok.extern.slf4j.Slf4j;
import org.gcu.filestorage.api.ErrorCode;
import org.gcu.filestorage.pojo.MinioProperties;
import org.gcu.filestorage.pojo.RequestResult;
import org.gcu.filestorage.uitls.TokenUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.util.ResourceUtils;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.multipart.MultipartFile;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.util.ArrayList;
import java.util.List;


@Slf4j
@RestController
public class FileController {

    private final MinioProperties minioProperties;

    private final MinioClient minioClient;


    @Autowired
    public FileController(MinioProperties minioProperties, MinioClient minioClient) {
        this.minioProperties = minioProperties;
        this.minioClient = minioClient;
    }

    /**
     * 新建容量申请成功后，为该账号创建一个Bucket
     *
     * @param account 申请账号
     * @return 新建容量是否成功
     */
    public boolean createBucket(String account) {
        String bucketName = account.toLowerCase();
        BucketExistsArgs bucketExistsArgs = BucketExistsArgs.builder().bucket(bucketName).build();
        try {
            if (!minioClient.bucketExists(bucketExistsArgs)) {
                MakeBucketArgs makeBucketArgs = MakeBucketArgs.builder().bucket(bucketName).build();
                this.minioClient.makeBucket(makeBucketArgs);
                uploadMetaFile(bucketName, "下载");
                uploadMetaFile(bucketName, "上传");
            }
        } catch (Exception e) {
            log.error("createBucket(), Error occurred: " + e);
            return false;
        }
        return true;
    }

    private void uploadMetaFile(String bucketName, String dirName) throws ServerException, InsufficientDataException, ErrorResponseException, IOException, NoSuchAlgorithmException, InvalidKeyException, InvalidResponseException, XmlParserException, InternalException {
        String fileName = ".G_STORE";
        String filePath = dirName + "/" + fileName;
        String GStorePath = "classpath:" + fileName;
        File file = ResourceUtils.getFile(GStorePath);
        FileInputStream inputStream = new FileInputStream(file);
        this.minioClient.putObject(PutObjectArgs.builder()
                .bucket(bucketName)
                .object(filePath)
                .stream(inputStream, file.length(), -1)
                .contentType("text/plain")
                .build());
        inputStream.close();
        String fileUrl = String.format("%s/%s/%s", this.minioProperties.getEndpoint(), bucketName, filePath);
        log.info("upload(), fileUrl=" + fileUrl);
    }

    @PostMapping("/upload")
    public RequestResult<String> upload(@RequestParam("token") String token,
                         @RequestParam("file") MultipartFile file,
                         @RequestParam("folderPath") String folderPath) {
        log.error("upload()");
        RequestResult<String> result = new RequestResult<>();
        String account = TokenUtil.parseAccount(token);
        if (token.isEmpty() || account.isEmpty()) {
            result.setData("");
            result.setMessage("");
            result.setResCode(ErrorCode.TOKEN_INCORRECT);
            return result;
        }
        String fileName = file.getOriginalFilename();
        String filePath = folderPath + "/" +fileName;
        String bucketName = account.toLowerCase();
        try {
            this.minioClient.putObject(PutObjectArgs.builder()
                    .bucket(bucketName)
                    .object(filePath)
                    .stream(file.getInputStream(), file.getSize(), -1)
                    .contentType(file.getContentType())
                    .build());
        } catch (Exception e) {
            log.error("upload(), Error occurred: " + e);
            result.setResCode(ErrorCode.UPLOAD_FILE_FAILURE);
            return result;
        }
        String fileUrl = String.format("%s/%s/%s", this.minioProperties.getEndpoint(), bucketName, filePath);
        log.info("upload(), fileUrl=" + fileUrl);
        result.setData(fileUrl);
        result.setResCode(ErrorCode.SUCCESS);
        return result;
    }

    @PostMapping("/listAllFilePath")
    public RequestResult<List<String>> listAllFilePath(@RequestHeader("Authorization") String token) {
        RequestResult<List<String>> requestResult = new RequestResult<>();
        List<String> filePathList = new ArrayList<>();
        String account = TokenUtil.parseAccount(token);
        try {
            ListObjectsArgs listObjectsArgs = ListObjectsArgs.builder().bucket(account).recursive(true).build();
            Iterable<Result<Item>> listObjects = this.minioClient.listObjects(listObjectsArgs);
            for (Result<Item> resultItem : listObjects) {
                Item item = resultItem.get();
                filePathList.add(item.objectName());
            }
        } catch (Exception e) {
            requestResult.setData(null);
            requestResult.setResCode(ErrorCode.LIST_FILE_PATH_FAILURE);
            requestResult.setMessage("列举存储文件失败");
            return requestResult;
        }
        log.info("listAllFilePath(), filePathList=" + filePathList);
        requestResult.setData(filePathList);
        requestResult.setResCode(ErrorCode.SUCCESS);
        requestResult.setMessage("列举存储文件成功");
        return requestResult;
    }

    @PostMapping("/newFolder")
    public RequestResult<Boolean> newFolder(@RequestHeader("Authorization") String token, @RequestParam("folderPath") String folderPath) {
        RequestResult<Boolean> requestResult = new RequestResult<>();
        String account = TokenUtil.parseAccount(token);
        String bucketName = account.toLowerCase();
        try {
            uploadMetaFile(bucketName, folderPath);
        } catch (Exception e) {
            requestResult.setData(false);
            requestResult.setResCode(ErrorCode.NEW_FOLDER_FAILURE);
            requestResult.setMessage("新建文件夹失败");
            return requestResult;
        }
        requestResult.setData(true);
        requestResult.setResCode(ErrorCode.SUCCESS);
        requestResult.setMessage("新建文件夹成功");
        return requestResult;
    }

    @PostMapping("/deleteFolder")
    public RequestResult<Boolean> deleteFolder(@RequestHeader("Authorization") String token, @RequestParam("folderPath") String folderPath) {
        RequestResult<Boolean> requestResult = new RequestResult<>();
        String account = TokenUtil.parseAccount(token);
        String bucketName = account.toLowerCase();
        try {
            Iterable<Result<Item>> list = minioClient.listObjects(ListObjectsArgs.builder()
                    .bucket(bucketName)
                    .prefix(folderPath)
                    .recursive(true)
                    .build());
            for(Result<Item> item: list){
                minioClient.removeObject(RemoveObjectArgs.builder()
                        .bucket(bucketName)
                        .object(item.get().objectName()).build());
            }
        } catch (Exception e) {
            requestResult.setData(false);
            requestResult.setResCode(ErrorCode.DELETE_FOLDER_FAILURE);
            requestResult.setMessage("删除文件夹失败");
            return requestResult;
        }
        requestResult.setData(true);
        requestResult.setResCode(ErrorCode.SUCCESS);
        requestResult.setMessage("删除文件夹成功");
        return requestResult;
    }

    @PostMapping("/deleteFile")
    public RequestResult<Boolean> deleteFile(@RequestHeader("Authorization") String token, @RequestParam("filePath") String filePath) {
        RequestResult<Boolean> requestResult = new RequestResult<>();
        String account = TokenUtil.parseAccount(token);
        String bucketName = account.toLowerCase();
        try {
                minioClient.removeObject(RemoveObjectArgs.builder()
                        .bucket(bucketName)
                        .object(filePath).build());
        } catch (Exception e) {
            requestResult.setData(false);
            requestResult.setResCode(ErrorCode.DELETE_FILE_FAILURE);
            requestResult.setMessage("删除文件失败");
            return requestResult;
        }
        requestResult.setData(true);
        requestResult.setResCode(ErrorCode.SUCCESS);
        requestResult.setMessage("删除文件成功");
        return requestResult;
    }
}
